﻿using System;
using System.Security.Cryptography;
using System.Text;
using XinghuaPack.Utility;
using System.Collections.Specialized;
using System.Web;
using System.Net;
using System.IO;

using NV = System.Collections.Specialized.NameValueCollection;
using UCPostData = System.Collections.Generic.Dictionary<string, object>;
using System.Diagnostics;

namespace UCenterLib {
    public class UCClient {
        static Encoding ISO_8859_1 = Encoding.GetEncoding("ISO-8859-1");
        static Encoding GB2312 = Encoding.GetEncoding("GB2312");
        static Encoding UC_ENCODING = Encoding.GetEncoding(UCConfig.UC_CHARSET);

        static readonly string USER_AGENT = "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.2)";
        /// <summary>
        /// 加密算法
        /// 说明:
        /// 1. PHP中处理中文一律使用的是GB2312???????????????, 所以这里也是用到了几处.
        /// 2. PHP中将chr直接做加法到变量, 可以得到中文, 但是.NET中不可以, 必须用GB2312.GetString来做; 
        ///    正因为如此, 在做Base64转换的时候也不能用字符串操作, 要用byte[]
        /// 3. 因为ENCODE最后要去掉Base64的=号, 所以要自己写一个自动补全等号的Base64Decode算法.
        /// </summary>
        /// <param name="source"></param>
        /// <param name="operation"></param>
        /// <param name="key"></param>
        /// <returns></returns>
        public static string AuthCode(string source, string operation, string key){
            if (string.IsNullOrEmpty(operation) ||
                (operation != "ENCODE" && operation != "DECODE")) {
                operation = "DECODE";
            }

            // int cKeyLength = 4;

            key = GetMD5(string.IsNullOrEmpty(key) ? UCConfig.UC_KEY : key);
            string keyA = GetMD5(key.Substring(0, 16));
            string keyB = GetMD5(key.Substring(16, 16));
            string keyC = operation == "DECODE" ? source.Substring(0, 4) : GetMD5(SecondsSince1970().ToString()).Substring(28, 4);

            string cryptKey = keyA + GetMD5(keyA + keyC);
            int keyLength = cryptKey.Length;

            byte[] sourceBytes = operation == "DECODE" ?
                Base64DecodeSafe(source.Substring(4)) :
                UC_ENCODING.GetBytes((SecondsSince1970() + 20).ToString("D10") + GetMD5(source + keyB).Substring(0, 16) + source);

            int sourceLength = sourceBytes.Length;

            // StringBuilder result = new StringBuilder();

            int[] box = new int[256];
            int[] rndKey = new int[256];
            char[] cryptKeyCharArr = cryptKey.ToCharArray();
            for (int i = 0; i < 256; i++) {
                box[i] = i;
                rndKey[i] = (int)(cryptKeyCharArr[i % keyLength]);
            }

            int tmp = 0;
            for (int j = 0, i = 0; i < 256; i++) {
                j = (j + box[i] + rndKey[i]) % 256;
                tmp = box[i];
                box[i] = box[j];
                box[j] = tmp;
            }

            byte[] resultBytes = new byte[sourceLength];

            for (int a = 0, j = 0, i = 0; i < sourceLength; i++) {
                a = (a + 1) % 256;
                j = (j + box[a]) % 256;
                tmp = box[a];
                box[a] = box[j];
                box[j] = tmp;
                resultBytes[i] = (byte)(sourceBytes[i] ^ (box[(box[a] + box[j]) % 256]));
//                 result.Append((char)(sourceBytes[i] ^ (box[(box[a] + box[j]) % 256])));
            }

            // string resultStr = result.ToString();
            string resultStr = UC_ENCODING.GetString(resultBytes);

            if (operation == "DECODE") {
                int f10 = int.Parse(resultStr.Substring(0, 10));
                if (f10 == 0 || f10 - SecondsSince1970() > 0 &&
                    resultStr.Substring(10, 16) == GetMD5(resultStr.Substring(26) + keyB).Substring(0, 16)) {
                    return resultStr.Substring(26);
                } else {
                    return string.Empty;
                }
            } else {
                // return keyC + Base64Encode(resultStr).Replace("=", string.Empty);
                return keyC + Convert.ToBase64String(resultBytes).Replace("=", string.Empty);
            }
        }

        /// <summary>
        /// 向UC_SERVER POST信息
        /// </summary>
        /// <param name="module"></param>
        /// <param name="action"></param>
        /// <param name="args"></param>
        /// <returns></returns>
        public static string ApiPost(string module, string action, NV args) {
            // NameValueCollection nvc = new NameValueCollection() { { "1", "2" }, { "3", "2" } };
            string argsQS = StringUtility.ConvertToQueryString(args);
            string postData = ApiRequestData(module, action, argsQS, string.Empty);

            string result = HttpPost(UCConfig.UC_API + "index.php", postData);

#if DEBUG
            EventLog.WriteEntry("Application", string.Format("\n[UCPost]\nmodule:{0}\naction:{1}\nargs:\n{2}\nresult:\n{3}", module, action, argsQS, result ), EventLogEntryType.Information);
#endif
            return result;
        }

        public static string ApiRequestData(string module, string action, string args, string extra) {
            string input = ApiInput(args);
            string post = string.Format("m={0}&a={1}&inajax=2&release={2}&input={3}&appid={4}", module, action, UCConfig.UC_CLIENT_RELEASE,input, UCConfig.UC_APPID) + extra;
            return post;
        }

        public static string ApiInput(string args) {
            return HttpUtility.UrlEncode(
                AuthCode(args + "&agent=" + GetMD5(USER_AGENT) + "&time=" + SecondsSince1970(), "ENCODE", UCConfig.UC_KEY));
        }

        public static string HttpPost(string uri, string body) {
            HttpWebRequest request = (HttpWebRequest)WebRequest.Create(uri);

            request.Method = "POST";
            byte[] postbytes = UC_ENCODING.GetBytes(body);
            // Set the content type of the data being posted.
            request.ContentType = "application/x-www-form-urlencoded";
            // Set the content length of the string being posted.
            request.ContentLength = postbytes.Length;

            // request.Referer = referer;
            request.UserAgent = USER_AGENT;
            request.Accept = "*/*";
            request.Headers[HttpRequestHeader.AcceptLanguage] = "zh-cn";
            request.Headers[HttpRequestHeader.CacheControl] = "no-cache";
            // request.Headers[HttpRequestHeader.AcceptEncoding] = "gzip, deflate";
            // request.Headers[HttpRequestHeader.Cookie] = cookie;

            // request.CookieContainer = cookies;

            request.Timeout = 5000;

            try {
                Stream streamSend = request.GetRequestStream();
                streamSend.Write(postbytes, 0, postbytes.Length);
                streamSend.Close();
            }
            catch (WebException ex) {
                throw ex;
            }

            string page;
            HttpWebResponse response;
            try {
                response = (HttpWebResponse)request.GetResponse();
                Stream streamRecv = response.GetResponseStream();
                StreamReader reader = new StreamReader(streamRecv, UC_ENCODING);
                page = reader.ReadToEnd();
            } catch (WebException ex) {
                throw ex;
            } catch (CookieException ex) {
                throw ex;
            } catch (IOException ex) {
                throw ex;
            }

            return page;
        }

        public static string GetMD5(string source) {
            HashAlgorithm hash = new MD5CryptoServiceProvider();
            byte[] hashedBytes = hash.ComputeHash(UC_ENCODING.GetBytes(source));
            StringBuilder result = new StringBuilder();
            foreach (byte x in hashedBytes) {
                result.Append(x.ToString("x2"));
            }
            return result.ToString();
        }

        /// <summary>
        /// 为了适应url, uc把末尾的=都去掉了, 这里要适时的补上
        /// </summary>
        /// <param name="source"></param>
        /// <returns></returns>
        public static byte[] Base64DecodeSafe(string source) {
            try {
                return Convert.FromBase64String(source);
            } catch (FormatException) {
                try {
                    return Convert.FromBase64String(source + "=");
                } catch (FormatException) {
                    return Convert.FromBase64String(source + "==");
                }
            }
        }

        public static string Base64Encode(string source) {
            return Convert.ToBase64String(ISO_8859_1.GetBytes(source));
        }

        /// <summary>
        /// 对应php里的time() + 0的数值
        /// </summary>
        /// <returns></returns>
        public static long SecondsSince1970() {
            return (long)(DateTime.UtcNow - new DateTime(1970, 1, 1)).TotalSeconds;
        }

        //public static string PHP_Microtime() {
        //    long sec = SecondsSince1970();
        //    int msec = DateTime.UtcNow.Millisecond;
        //    string msecStr = "0." + msec.ToString().PadRight(8, '0');
        //    return msecStr + " " + sec.ToString();
        //}

        public static int UCUserRegister(string username, string password, string email) {
            string result = ApiPost("user", "register", new NameValueCollection(){
                    {"username", HttpUtility.UrlEncode(username, Encoding.GetEncoding(UCConfig.UC_CHARSET))},
                    {"password", password},
                    {"email", email}
                });
            return Convert.ToInt32(result);
        }

        public static int UCUserRegister(string username, string password, string email, int questionid, string answer) {
            string result = ApiPost("user", "register", new NameValueCollection(){
                    {"username", HttpUtility.UrlEncode(username, Encoding.GetEncoding(UCConfig.UC_CHARSET))},
                    {"password", password},
                    {"email", email},
                    {"questionid", questionid.ToString()},
                    {"answer", HttpUtility.UrlEncode(answer)}
                });
            return Convert.ToInt32(result);
        }

        public static UCPostData UCUserLogin(string username, string password, int userId, bool checkQuestion, int questionId, string answer) {
            return UCXmlSerialization.Deserialize(
                ApiPost("user", "login", new NameValueCollection(){
                    {"username", HttpUtility.UrlEncode(username, Encoding.GetEncoding(UCConfig.UC_CHARSET))},
                    {"password", password},
                    {"isuid", userId.ToString()},
                    {"checkques", checkQuestion ? "1" : "0"},
                    {"questionid", questionId.ToString()},
                    {"answer", HttpUtility.UrlEncode(answer)}
                }));
        }

        public static UCPostData UCGetUser(string username, int userId) {
            string result = ApiPost("user", "get_user", new NameValueCollection(){
                    {"username", HttpUtility.UrlEncode(username, Encoding.GetEncoding(UCConfig.UC_CHARSET))},
                    {"isuid", userId.ToString()}
                });
            if (result == "0") {
                return null;
            } else {
                return UCXmlSerialization.Deserialize(result);
            }
        }

        public static string UCSynLogin(int userId) {
            string result = ApiPost("user", "synlogin", new NameValueCollection(){
                    {"uid", userId.ToString()}
            });
            return result;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="username"></param>
        /// <returns>
        /// 1:成功
        /// -1:用户名不合法
        /// -2:包含要允许注册的词语
        /// -3:用户名已经存在
        /// </returns>
        public static int UCCheckUser(string username) {
            if (string.IsNullOrEmpty(username)) {
                return -1;
            }
            string result = ApiPost("user", "check_username", new NameValueCollection(){
                    {"username", username}
            });
            // return result;
            int resultInt32 = -99;
            if (!int.TryParse(result, out resultInt32)) {
                return -99;
            }
            return resultInt32;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="email"></param>
        /// <returns>
        /// 1:成功
        /// -4:Email 格式有误
        /// -5:Email 不允许注册
        /// -6:该 Email 已经被注册
        /// </returns>
        public static int UCCheckEmail(string email) {
            if (string.IsNullOrEmpty(email)) {
                return -1;
            }
            string result = ApiPost("user", "check_email", new NameValueCollection(){
                    {"email", email}
            });
            // return result;
            int resultInt32 = -99;
            if (!int.TryParse(result, out resultInt32)) {
                return -99;
            }
            return resultInt32;
        }

        ///* ** **异步 ** *****/

        //public IAsyncResult BeginUCGetUser(AsyncCallback callback, string username, int userId) {
        //    return BeginApiPost(callback, "user", "get_user", new NameValueCollection(){
        //        {"username", HttpUtility.UrlEncode(username, Encoding.GetEncoding(UCConfig.UC_CHARSET))},
        //        {"isuid", userId.ToString()}
        //    });
        //}

        //public UCPostData EndUCGetUser(IAsyncResult result) {
        //    string resultString = this.EndHttpPost(result);
        //    if (resultString == "0") {
        //        return null;
        //    }
        //    else {
        //        return UCXmlSerialization.Deserialize(resultString);
        //    }
        //}

        //IAsyncResult BeginApiPost(AsyncCallback callback, string module, string action, NV args) {
        //    string argsQS = StringUtility.ConvertToQueryString(args);
        //    string postData = ApiRequestData(module, action, argsQS, string.Empty);

        //    return BeginHttpPost(callback, UCConfig.UC_API + "index.php", postData);
        //}

        //string EndApiPost(IAsyncResult result) {
        //    return EndHttpPost(result);
        //}

        //IAsyncResult BeginHttpPost(AsyncCallback callback, string uri, string body) {
        //    HttpWebRequest request = (HttpWebRequest)WebRequest.Create(uri);
        //    request.Method = "POST";
        //    byte[] postbytes = GB2312.GetBytes(body);
        //    // Set the content type of the data being posted.
        //    request.ContentType = "application/x-www-form-urlencoded";
        //    // Set the content length of the string being posted.
        //    request.ContentLength = postbytes.Length;

        //    // request.Referer = referer;
        //    request.UserAgent = "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.2)";
        //    request.Accept = "*/*";
        //    request.Headers[HttpRequestHeader.AcceptLanguage] = "zh-cn";
        //    request.Headers[HttpRequestHeader.CacheControl] = "no-cache";
        //    // request.Headers[HttpRequestHeader.AcceptEncoding] = "gzip, deflate";
        //    // request.Headers[HttpRequestHeader.Cookie] = cookie;

        //    // request.CookieContainer = cookies;

        //    request.Timeout = 5000;

        //    Stream streamSend = request.GetRequestStream();
        //    streamSend.Write(postbytes, 0, postbytes.Length);
        //    streamSend.Close();

        //    return request.BeginGetResponse(callback, null);

        //}

        //string EndHttpPost(IAsyncResult result) {
        //    HttpWebResponse response = (HttpWebResponse)(result.AsyncState as HttpWebRequest).EndGetResponse(result);
        //    using (Stream streamRecv = response.GetResponseStream()) {
        //        StreamReader reader = new StreamReader(streamRecv, GB2312);
        //        return reader.ReadToEnd();
        //    }
        //}
    }
}
